﻿using log4net;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SharpML.Adaptors.MNIST
{
    public class Utility 
    {
        /// <summary>
        /// Simple logging pattern
        /// </summary>
        private static readonly ILog Log = LogManager.GetLogger( System.Reflection.MethodBase.GetCurrentMethod().DeclaringType );

        // TODO: This assumes a lot, was written quickly a long time ago and dropped into this project.
        public static List<DataItem> LoadFile( string basePath, bool trainingData, int maxSamples = Int32.MaxValue )
        {
            // Locals
            List<DataItem> results = new List<DataItem>();

            try
            {
                // Locals
                string namePart = trainingData ? "train" : "t10k";
                string filename = Path.Combine( basePath, namePart + "-{0}.{1}-ubyte" );

                // Load labels
                using( FileStream fs = new FileStream( String.Format( filename, "labels", "idx1" ), FileMode.Open ) )
                {
                    // Locals
                    byte[] oneByte = new byte[1];
                    byte[] trash = new byte[8];

                    // Read trash
                    fs.Read( trash, 0, 8 );

                    // Read Labels
                    int index = 0;

                    // TODO: Off-by-one bug
                    while( index++ < maxSamples && fs.Position < fs.Length )
                    {
                        fs.Read( oneByte, 0, 1 );
                        results.Add( new DataItem { Label = (int)oneByte[0] } );
                    }
                }

                // Load labels
                using( FileStream fs = new FileStream( String.Format( filename, "images", "idx3" ), FileMode.Open ) )
                {
                    // Locals
                    byte[] pixelBytes = new byte[784];
                    float[,] imageData; // = new float[28, 28];
                    float[] imageDataFlat; // = new float[784];
                    byte[] trash = new byte[16];

                    // Read trash
                    fs.Read( trash, 0, 16 );


                    // Read Images
                    int index = 0;
                    while( index < maxSamples && fs.Position < fs.Length )
                    {
                        // Read
                        fs.Read( pixelBytes, 0, 784 );

                        // Convert
                        imageData = new float[28, 28];
                        imageDataFlat = new float[784];
                        for( int x = 0, y = 0, z = 0; z < 784; y += ( z + 1 ) % 28 == 0 ? 1 : 0, x = ( z + 1 ) % 28, z++ )
                        {
                            imageData[x, y] = (float)pixelBytes[z] / 255.0f;
                            imageDataFlat[z] = imageData[x, y];
                        }

                        // Store
                        results[index].Image = imageData;
                        results[index].ImageData = imageDataFlat;
                    }
                }
            }

            catch( Exception exception )
            {
                Log.Error( "Could not load MNIST dataset from " + basePath, exception );
            }


            //results = results.Where( item => item.Label == 5 ).ToList();

            // Return results
            return results;
        }


        public class DataItem
        {
            public float[,] Image;
            public float[] ImageData;
            public int Label;
        }
    }
}
